package com.andova.toolkit.util;

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.media.ExifInterface;
import android.os.Build;
import android.os.Environment;
import android.text.TextUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import androidx.annotation.Nullable;

/**
 * Created by Administrator on 2017-10-10.
 *
 * @author kzaxil
 * @since 1.0.0
 */
public class ImageUtil {
    /**
     * 加载本地图片
     */
    public static Bitmap getLocalBitmap(String url) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(url);
            return BitmapFactory.decodeStream(fis);
        } catch (FileNotFoundException | NullPointerException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                //noinspection ConstantConditions
                if (fis != null) fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Bitmap转字符数组
     */
    public static byte[] bitmapToBytes(Bitmap bitmap) {
        if (bitmap == null) return null;

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        // scale
        int options = 100;
        // Store the bitmap into output stream(no compress)
        bitmap.compress(Bitmap.CompressFormat.JPEG, options, os);

        try {
            return os.toByteArray();
        } catch (NullPointerException e) {
            e.printStackTrace();
        } finally {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 压缩图片
     *
     * @param path  待压缩图片的路径
     * @param maxKB 准备将图片压缩在多少KB以内
     * @return 返回压缩后的图片路径
     */
    public static String compressImage(String path, int maxKB) {
        return compressImage(path, 0, 0, maxKB);
    }

    public static String compressImage(String path, int targetWidth, int targetHeight, int maxKB) {
        Bitmap bmp;
        if (targetWidth <= 0 || targetHeight <= 0) {
            bmp = getLocalBitmap(path);
        } else {
            bmp = zoomBitmap(getLocalBitmap(path), targetWidth, targetHeight);
        }
        if (bmp == null) return path;
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        // scale
        int options = 100;
        // Store the bitmap into output stream(no compress)
        bmp.compress(Bitmap.CompressFormat.JPEG, options, os);
        // Compress by loop
        while (os.toByteArray().length / 1024 > maxKB) {
            if (options <= 10) break;
            // Clean up os
            os.reset();
            // interval 10
            options -= 10;
            bmp.compress(Bitmap.CompressFormat.JPEG, options, os);
        }

        // Generate compressed image file
        FileOutputStream fos;
        try {
            fos = new FileOutputStream(path);
            fos.write(os.toByteArray());
            fos.flush();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                os.close();
            } catch (IOException | NullPointerException e) {
                e.printStackTrace();
            }
        }
        return path;
    }

    public static byte[] compressBitmap(String path, int targetWidth, int targetHeight, int maxKB) {
        Bitmap bmp;
        if (targetWidth <= 0 || targetHeight <= 0) {
            bmp = getLocalBitmap(path);
        } else {
            bmp = zoomBitmap(getLocalBitmap(path), targetWidth, targetHeight);
        }
        if (bmp == null) return null;

        return compressBitmap(bmp, targetWidth, targetHeight, maxKB);
    }

    public static byte[] compressBitmap(Bitmap bitmap, int targetWidth, int targetHeight, int maxKB) {
        if (bitmap == null) return null;
        if (targetWidth > 0 && targetHeight > 0) {
            bitmap = zoomBitmap(bitmap, targetWidth, targetHeight);
        }

        return compressBitmap(bitmap, maxKB);
    }

    public static byte[] compressBitmap(Bitmap bitmap, int maxKB) {
        if (bitmap == null) return null;

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        // scale
        int options = 100;
        // Store the bitmap into output stream(no compress)
        bitmap.compress(Bitmap.CompressFormat.JPEG, options, os);
        // Compress by loop
        while (os.toByteArray().length / 1024 > maxKB) {
            if (options <= 10) break;
            // Clean up os
            os.reset();
            // interval 10
            options -= 10;
            bitmap.compress(Bitmap.CompressFormat.JPEG, options, os);
        }

        try {
            return os.toByteArray();
        } catch (NullPointerException e) {
            e.printStackTrace();
        } finally {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Nullable
    public static String scaleImage(String path, int maxKB) {
        Bitmap bitmap = scaleBitmap(path, maxKB);
        if (bitmap == null) return null;
        return bitmapToFile(bitmap, path);
    }

    @Nullable
    public static Bitmap scaleBitmap(String path, int maxKB) {
        if (TextUtils.isEmpty(path)) return null;
        Bitmap bmp = getLocalBitmap(path);
        if (bmp == null) return null;
        int size = FileSizeUtil.getFileSize(path);
        if (size <= 0) return null;
        if (maxKB >= size) return bmp;
        // 获取这个图片的宽和高
        float width = bmp.getWidth();
        float height = bmp.getHeight();
        // 创建操作图片用的matrix对象
        Matrix matrix = new Matrix();
        // 计算宽高缩放率
        float scale = ((float) maxKB) / size;
        // 缩放图片动作
        matrix.postScale(scale, scale);
        try {
            return Bitmap.createBitmap(bmp, 0, 0, (int) width, (int) height, matrix, true);
        } catch (IllegalArgumentException e) {
            return null;
        }
    }

    public static Bitmap zoomBitmap(Bitmap bmp, double targetWidth, double targetHeight) {
        if (bmp == null) return null;
        // 获取这个图片的宽和高
        float width = bmp.getWidth();
        float height = bmp.getHeight();
        // 创建操作图片用的matrix对象
        Matrix matrix = new Matrix();
        // 计算宽高缩放率
        float scaleWidth = ((float) targetWidth) / width;
        float scaleHeight = ((float) targetHeight) / height;
        // 缩放图片动作
        matrix.postScale(scaleWidth, scaleHeight);
        return Bitmap.createBitmap(bmp, 0, 0, (int) width, (int) height, matrix, true);
    }

    /**
     * 把图片缩小到长边不大于maxPx
     */
    public static Bitmap zoomBitmap(Bitmap bmp, int maxPx) {
        // 获取这个图片的宽和高
        float width = bmp.getWidth();
        float height = bmp.getHeight();
        // 创建操作图片用的matrix对象
        Matrix matrix = new Matrix();
        float scale = 1;
        float maxL = 1;
        // 计算宽高缩放率,如果width <maxPx，则保持原有的尺寸
        if (width > height) {
            maxL = width;
        } else if (height > maxPx) {
            maxL = height;
        }

        if (maxL > maxPx) {
            scale = maxPx / maxL;
        }

        // 缩小图片动作
        matrix.postScale(scale, scale);
        return Bitmap.createBitmap(bmp, 0, 0, (int) width, (int) height, matrix, true);
    }

    @Nullable
    public static Bitmap bitmapToFile(Context context, Bitmap bmp, Bitmap.CompressFormat compressFormat, String fileName) {
        if (context == null) return null;
        return bitmapToFile(bmp, compressFormat, context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName);
    }

    @Nullable
    public static Bitmap bitmapToFile(Bitmap bmp, Bitmap.CompressFormat compressFormat, @Nullable File fileDir, String fileName) {
//        File appDir = new File(Environment.getExternalStorageDirectory(), fileDir);
        if (fileDir == null) return null;
        if (!fileDir.exists() && !fileDir.mkdir()) {
            return null;
        }
        if (TextUtils.isEmpty(fileName)) {
            if (compressFormat == Bitmap.CompressFormat.JPEG) {
                fileName = System.currentTimeMillis() + ".jpg";
            } else if (compressFormat == Bitmap.CompressFormat.PNG) {
                fileName = System.currentTimeMillis() + ".png";
            } else if (compressFormat == Bitmap.CompressFormat.WEBP) {
                fileName = System.currentTimeMillis() + ".webp";
            }
        }
        File file = new File(fileDir, fileName);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            if (compressFormat == Bitmap.CompressFormat.JPEG) {
                bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            } else if (compressFormat == Bitmap.CompressFormat.PNG) {
                bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
            } else if (compressFormat == Bitmap.CompressFormat.WEBP) {
                bmp.compress(Bitmap.CompressFormat.WEBP, 100, fos);
            }
            fos.flush();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return bmp;
    }

    @Nullable
    public static String bitmapToFile(Bitmap bmp, String path) {
        return bitmapToFile(bmp, Bitmap.CompressFormat.JPEG, path);
    }

    @Nullable
    public static String bitmapToFile(Bitmap bmp, Bitmap.CompressFormat compressFormat, String path) {
        if (TextUtils.isEmpty(path)) return null;
        File file = new File(path);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            if (compressFormat == Bitmap.CompressFormat.JPEG) {
                bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            } else if (compressFormat == Bitmap.CompressFormat.PNG) {
                bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
            } else if (compressFormat == Bitmap.CompressFormat.WEBP) {
                bmp.compress(Bitmap.CompressFormat.WEBP, 100, fos);
            }
            fos.flush();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return path;
    }

    /**
     * 将bitmap中的某种颜色值替换成新的颜色
     */
    public static Bitmap replaceBitmapColor(Bitmap oldBitmap, int oldColor, int newColor) {
        Bitmap mBitmap = oldBitmap.copy(Bitmap.Config.ARGB_8888, true);
        //循环获得bitmap所有像素点
        int mBitmapWidth = mBitmap.getWidth();
        int mBitmapHeight = mBitmap.getHeight();
        for (int i = 0; i < mBitmapHeight; i++) {
            for (int j = 0; j < mBitmapWidth; j++) {
                // 获得Bitmap 图片中每一个点的color颜色值
                // 将需要填充的颜色值如果不是
                // 在这说明一下 如果color 是全透明 或者全黑 返回值为 0
                // getPixel()不带透明通道 getPixel32()才带透明部分 所以全透明是0x00000000
                // 而不透明黑色是0xFF000000 如果不计算透明部分就都是0了
                int color = mBitmap.getPixel(j, i);
                // 将颜色值存在一个数组中 方便后面修改
                if (color == oldColor) {
                    mBitmap.setPixel(j, i, newColor);  //将白色替换成透明色
                }
            }
        }
        return mBitmap;
    }

    /**
     * 设置Bitmap的背景色，如默认情况下，将Bitmap保存成图片时，生成的图片背景是黑色的，这个时候，就可以用该方法来替换背景颜色
     */
    public static Bitmap replaceBitmapBackground(int color, Bitmap originBitmap) {
        Paint paint = new Paint();
        paint.setColor(color);
        Bitmap bitmap = Bitmap.createBitmap(originBitmap.getWidth(),
                originBitmap.getHeight(), originBitmap.getConfig());
        Canvas canvas = new Canvas(bitmap);
        canvas.drawRect(0, 0, originBitmap.getWidth(), originBitmap.getHeight(), paint);
        canvas.drawBitmap(originBitmap, 0, 0, paint);
        return bitmap;
    }

    /**
     * 旋转图片
     *
     * @param angle  被旋转角度
     * @param bitmap 图片对象
     * @return 旋转后的图片
     */
    public static Bitmap rotateImage(int angle, Bitmap bitmap) {
        Bitmap returnBm;
        // 根据旋转角度，生成旋转矩阵
        Matrix matrix = new Matrix();
        matrix.postRotate(angle);
        try {
            // 将原始图片按照旋转矩阵进行旋转，并得到新的图片
            returnBm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
            return null;
        }
        return returnBm;
    }

    /**
     * 将相机采集到的图像数据转为Bitmap
     */
    @Nullable
    public static Bitmap decodeToBitmap(byte[] data, Camera camera) {
        return decodeToBitmap(true, data, camera);
    }

    /**
     * 将相机采集到的图像数据转为Bitmap
     *
     * @param isFront 是否是前置摄像头
     */
    @Nullable
    public static Bitmap decodeToBitmap(boolean isFront, byte[] data, Camera camera) {
        if (data == null || camera == null) return null;
        ByteArrayOutputStream stream = null;
        try {
            Camera.Size size = camera.getParameters().getPreviewSize();
            int imageW = size.height;// 旋转90/270度后，解码器的宽度值因和之前的高度值互换
            int imageH = size.width;// 旋转90/270度后，解码器的高度值因和之前的宽度值互换
            YuvImage image = new YuvImage(isFront
                    ? rotateYUVDegree270AndMirror(data, size.width, size.height)
                    : rotateYUVDegree90(data, size.width, size.height),
                    ImageFormat.NV21, imageW, imageH, null);
            stream = new ByteArrayOutputStream();
            image.compressToJpeg(new Rect(0, 0, imageW, imageH), 100, stream);
            return BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());
        } catch (Exception e) {
            return null;
        } finally {
            try {
                if (stream != null) stream.close();
            } catch (IOException ignored) {
            }
        }
    }

    /**
     * 将相机采集到的图像数据转为Bitmap
     *
     * @param isFront 是否是前置摄像头
     */
    @Nullable
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static Bitmap decodeToBitmapV19(boolean isFront, byte[] data, Camera camera) {
        if (data == null || camera == null) return null;
        try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
            Camera.Size size = camera.getParameters().getPreviewSize();
            int imageW = size.height;// 旋转90/270度后，解码器的宽度值因和之前的高度值互换
            int imageH = size.width;// 旋转90/270度后，解码器的高度值因和之前的宽度值互换
            YuvImage image = new YuvImage(isFront
                    ? rotateYUVDegree270AndMirror(data, size.width, size.height)
                    : rotateYUVDegree90(data, size.width, size.height),
                    ImageFormat.NV21, imageW, imageH, null);
            image.compressToJpeg(new Rect(0, 0, imageW, imageH), 100, stream);
            return BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 一般后置摄像头有90度的旋转
     */
    public static byte[] rotateYUVDegree90(byte[] data, int imageWidth, int imageHeight) {
        byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
        // Rotate the Y luma
        int i = 0;
        for (int x = 0; x < imageWidth; x++) {
            for (int y = imageHeight - 1; y >= 0; y--) {
                yuv[i] = data[y * imageWidth + x];
                i++;
            }
        }
        // Rotate the U and V color components
        i = imageWidth * imageHeight * 3 / 2 - 1;
        for (int x = imageWidth - 1; x > 0; x = x - 2) {
            for (int y = 0; y < imageHeight / 2; y++) {
                yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + x];
                i--;
                yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + (x - 1)];
                i--;
            }
        }
        return yuv;
    }

    public static byte[] rotateYUVDegree270(byte[] data, int imageWidth, int imageHeight) {
        byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
        // Rotate the Y luma
        int i = 0;
        for (int x = imageWidth - 1; x >= 0; x--) {
            for (int y = 0; y < imageHeight; y++) {
                yuv[i] = data[y * imageWidth + x];
                i++;
            }
        }
        // Rotate the U and V color components
        i = imageWidth * imageHeight;
        for (int x = imageWidth - 1; x > 0; x = x - 2) {
            for (int y = 0; y < imageHeight / 2; y++) {
                yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + (x - 1)];
                i++;
                yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + x];
                i++;
            }
        }
        return yuv;
    }

    /**
     * 一般前置摄像头有270度的旋转，所以要对YUV数据进行一定旋转操作,同时对于前置摄像头的数据还要进行镜像翻转的操作
     */
    private static byte[] rotateYUVDegree270AndMirror(byte[] data, int imageWidth, int imageHeight) {
        byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
        // Rotate and mirror the Y luma
        int i = 0;
        int maxY;
        for (int x = imageWidth - 1; x >= 0; x--) {
            maxY = imageWidth * (imageHeight - 1) + x * 2;
            for (int y = 0; y < imageHeight; y++) {
                yuv[i] = data[maxY - (y * imageWidth + x)];
                i++;
            }
        }
        // Rotate and mirror the U and V color components
        int uvSize = imageWidth * imageHeight;
        i = uvSize;
        int maxUV;
        for (int x = imageWidth - 1; x > 0; x = x - 2) {
            maxUV = imageWidth * (imageHeight / 2 - 1) + x * 2 + uvSize;
            for (int y = 0; y < imageHeight / 2; y++) {
                yuv[i] = data[maxUV - 2 - (y * imageWidth + x - 1)];
                i++;
                yuv[i] = data[maxUV - (y * imageWidth + x)];
                i++;
            }
        }
        return yuv;
    }

    public static Bitmap getPortraitBitmap(String path) {
        Bitmap bitmap = getLocalBitmap(path);
        if (bitmap == null) return null;
        // 得到图片的旋转角度
        int degree = getBitmapDegree(path);
        // 根据旋转角度，生成旋转矩阵
        Matrix matrix = new Matrix();
        matrix.postRotate(degree);
        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }


    /**
     * 获取原始图片的角度（解决三星手机拍照后图片是横着的问题）
     *
     * @param path 图片的绝对路径
     * @return 原始图片的角度
     */
    private static int getBitmapDegree(String path) {
        int degree = 0;
        try {
            // 从指定路径下读取图片，并获取其EXIF信息
            ExifInterface exifInterface = new ExifInterface(path);
            // 获取图片的旋转信息
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return degree;
    }
}
